/***********************************************************************

 Copyright (c) 2009, Luke Malcolm, www.lukemalcolm.com
 Based on ofxFileStream by Todd Vanderlin, www.vanderlin.cc

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 ***********************************************************************/

#include "ofxSMS.h"

#include <iostream>
#include <fstream>

// ---------------------------------------
ofxSMS::ofxSMS() {
	bSearching  = false;
	bAllocated  = false;
	bVerbose    = true;
	bMoveFiles  = true;

	fileIDCount = 0;
	numFiles    = 0;
}
ofxSMS::~ofxSMS() {
	files.clear();
	reset();
}

// ---------------------------------------
void ofxSMS::setup(string path, float seconds) {

	inboxPath  = path;
	outboxPath = path + "/outbox/";
	timer.setup(seconds, true);

	//events
	ofAddListener(timer.TIMER_REACHED, this, &ofxSMS::_checkForNewFiles);
	ofAddListener(NEW_SMS, this, &ofxSMS::newSMSEvent);

	bAllocated = true;

}

// ---------------------------------------
void ofxSMS::setTimer(float seconds) {
	timer.setTimer(seconds);
}

void ofxSMS::_processSMSFile(int id, string name, string path)
{
    ofxSMSMessage tmpMsg;

    //generate a UUID for the message and then put a friendly format
    uuid_generate_random(tmpMsg.raw_id);

    char tmpIdChar[36+1];
    uuid_unparse(tmpMsg.raw_id, tmpIdChar);
    tmpMsg.id    = tmpIdChar;   //convert from Char to String.
    tmpMsg.bRead = false;

    //set number based on filename pattern.
    vector<string> fileParams = ofSplitString(name,"_");
    int fileParamsN = fileParams.size();

    if (fileParamsN > 1) {
        tmpMsg.from = fileParams[1];
    }
    fileParams.clear();


    //set time and date from LINUX file attributes of the SMS file (not the SMS itself)
    struct stat attrib;
    stat(path.c_str(), &attrib);		            // get the attributes of the SMS file
    tmpMsg.datetime = attrib.st_mtime;

    //set SMS text based on file contents
    string line = "";

      ifstream SMSfile (path.c_str());
      if (SMSfile.is_open())
      {
        while (! SMSfile.eof() )
        {
          getline (SMSfile,line);
          if (line.size() > 1) {

            tmpMsg.text += line;

          }

        }
        if (tmpMsg.text.size() <= 1) tmpMsg.text = "*BLANK*";

        unread.push_back(tmpMsg);
        all.push_back(tmpMsg);

        SMSfile.close();

        ofLog(OF_LOG_VERBOSE,"Added SMS [%s]:%s  %s All Pos: %i, Unread Pos: %i\n", tmpMsg.id.c_str(), tmpMsg.from.c_str(), tmpMsg.text.c_str(), all.size(), unread.size());


        bool bCompleteDirExist = false;
        string completePath = inboxPath+"/complete";

        if (!_fileExists(completePath)) {
            int result = mkdir(completePath.c_str(), O_CREAT|S_IRWXG|S_IRWXU|S_IRWXO);
            if(result==0) {
                bCompleteDirExist = true;
            }
        }
        else {
            bCompleteDirExist = true;
        }


        if(bMoveFiles && bCompleteDirExist) {

            char new_filename[150];
            sprintf (new_filename,"%s/%s",completePath.c_str(),name.c_str());

            ofLog(OF_LOG_VERBOSE,"Renaming %s to %s\n",path.c_str(),new_filename);
            rename(path.c_str(),new_filename);

        }


        //trigger the POCO event
        static ofxSMSEventArgs SMSEventArgs;
        uuid_copy(SMSEventArgs.raw_id, tmpMsg.raw_id);
        SMSEventArgs.id          = tmpMsg.id;
        SMSEventArgs.allPosition = all.size();
        SMSEventArgs.unreadPosition = unread.size();

        ofNotifyEvent(NEW_SMS, SMSEventArgs, this);

      }

      else cout << "Unable to open file\n";

}

// ---------------------------------------
void ofxSMS::_checkForNewFiles(ofEventArgs &e) {

    bVerbose = true;

    //If not already searching and the SMS folder path exists, then interrogate the directory
	if(!bSearching && _fileExists(inboxPath)) {

		bSearching = true;

		int numFilesFound = listDir(inboxPath);

		if(numFilesFound == -1) {
			ofLog(OF_LOG_ERROR,"ofxSMS::_checkForNewFiles - We lost the path connection ---\n");
			ofLog(OF_LOG_ERROR,"ofxSMS::_checkForNewFiles - path:%s\n ---\n", inboxPath.c_str());
			reset();
			return;
		}
		if(numFilesFound) {

			for(int i=0; i<numFilesFound; i++) {

                //Check if this is a directory or file;
                bool isDirectory = false;
                struct stat attrib;
                stat(getPath(i).c_str(), &attrib); // get the attributes of the SMS file
                isDirectory = S_ISDIR(attrib.st_mode);

                //Check if this is a new file.
				bool fileFound = false;
				for(int j=0; j<files.size(); j++) {
					if(getName(i) == files[j]) fileFound = true;
				}

				// if we found the file in the files
				// vector the do not add it else add it
				if(!fileFound && !isDirectory) {
				    files.push_back(getName(i));
					static ofxSMSEventArgs fileEventArgs;
					_processSMSFile(fileIDCount,getName(i),getPath(i));
					fileIDCount ++;
					numFiles ++;
				}
			}

			//debug
			if(bVerbose) {
				debugText = "";
				debugText += "\nFiles Array\n";
				debugText += "------------------------------\n";
				for(int j=0; j<files.size(); j++) {
					debugText += "File: "+files[j]+"\n";
				}

                debugText += "\n";
				debugText += "\nAll SMS Array\n";
				debugText += "------------------------------\n";
				for(int j=0; j<all.size(); j++) {
					debugText += "SMS: " + ofToString(j) + " " + ofToString(all[j].bRead) + " " + all[j].id + " - " + all[j].text + " - " +asctime(localtime(&all[j].datetime))+"";

				}

                debugText += "\n";
				debugText += "\nUnread SMS Array\n";
				debugText += "------------------------------\n";
				for(int j=0; j<unread.size(); j++) {
					debugText += "SMS: " + ofToString(j) + " " + unread[j].id + " - " + unread[j].text + " - " +asctime(localtime(&unread[j].datetime))+"";

				}

                debugText += "\n";
				debugText += "\nRead SMS Array\n";
				debugText += "------------------------------\n";
				for(int j=0; j<read.size(); j++) {
					debugText += "SMS: " + ofToString(j) + " " + read[j].id + " - " + read[j].text + " - " +asctime(localtime(&read[j].datetime))+"";

				}

			}

		}
		else {
			if(bVerbose) debugText += "No Files Found\n";
		}
		bSearching = false;
	}

}

// ---------------------------------------
void ofxSMS::setPath(string p) {
	inboxPath = p;
	bAllocated = true;
}

// ---------------------------------------
void ofxSMS::draw(float x, float y) {

	ofSetColor(0xffffff);
	ofDrawBitmapString(debugText, 20, 20);
}

bool ofxSMS::_fileExists(string strFilename) {
  struct stat stFileInfo;
  bool blnReturn;
  int intStat;

  // Attempt to get the file attributes
  intStat = stat(strFilename.c_str(),&stFileInfo);
  if(intStat == 0) {
    // We were able to get the file attributes
    // so the file obviously exists.
    blnReturn = true;
  } else {

    blnReturn = false;
  }

  return(blnReturn);
}

void ofxSMS::_updateReadFlags(){

    //mark all the items in the ALL queue which are also int the READ queue as Read.
    std::vector<ofxSMSMessage>::iterator readSMSMessage;
    for(readSMSMessage = read.begin(); readSMSMessage != read.end(); readSMSMessage++){

        readSMSMessage->bRead = true;

        //now change the all message accordingly.
        std::vector<ofxSMSMessage>::iterator allSMSMessage;
        for(allSMSMessage = all.begin(); allSMSMessage != all.end(); allSMSMessage++){
            if (allSMSMessage->id == readSMSMessage->id) {
                allSMSMessage->bRead = true;
            }
        }

    }

    //mark all the items in the ALL queue which are also int the UNREAD queue as UnRead.
    std::vector<ofxSMSMessage>::iterator unreadSMSMessage;
    for(unreadSMSMessage = unread.begin(); unreadSMSMessage != unread.end(); unreadSMSMessage++){

        unreadSMSMessage->bRead = false;

        //now change the all message accordingly.
        std::vector<ofxSMSMessage>::iterator allSMSMessage;
        for(allSMSMessage = all.begin(); allSMSMessage != all.end(); allSMSMessage++){
            if (allSMSMessage->id == unreadSMSMessage->id) {
                allSMSMessage->bRead = false;
            }
        }

    }


}

bool ofxSMS::_sendToSpoolDir(ofxSMSMessage outSMS) {


    bool bOutboxDirExist = false;

    if (!_fileExists(outboxPath)) {
        ofLog(OF_LOG_VERBOSE,"ofxSMS::_sendToSpoolDir: Outbox directory %s found, creating directory.",outboxPath.c_str());
        int result = mkdir(outboxPath.c_str(), O_CREAT|S_IRWXG|S_IRWXU|S_IRWXO);
        if(result==0) {
            bOutboxDirExist = true;
        }
    }
    else {
        bOutboxDirExist = true;
    }

    if (bOutboxDirExist){
        ofstream outSMSfile;
        string filename = outboxPath + "/" + outSMS.id;
        outSMSfile.open (filename.c_str());
        outSMSfile << outSMS.to + "\n" + outSMS.text.c_str() + "\n";
        outSMSfile.close();

        ofLog(OF_LOG_VERBOSE,"ofxSMS::_sendToSpoolDir: Created file in outbox: %s",filename.c_str());
        return true;
    }
    else {
        return false;
    }


}

//-------- Public Functions

bool ofxSMS::send(string toNumber, string message){

    ofxSMSMessage outMsg;

    //generate a UUID for the message and then put a friendly format
    uuid_generate_random(outMsg.raw_id);

    char tmpIdChar[36+1];
    uuid_unparse(outMsg.raw_id, tmpIdChar);
    outMsg.id    = tmpIdChar;   //convert from Char to String.
    outMsg.bRead = false;
    outMsg.to    = toNumber;
    outMsg.text  = message;

    return _sendToSpoolDir(outMsg);
}


ofxSMSMessage ofxSMS::getNextUnreadSMS(){
    if(unread.size()>0){
        ofxSMSMessage tmpSMS = unread.front();
        tmpSMS.bRead = true;
        unread.erase(unread.begin());
        read.push_back(tmpSMS);

        _updateReadFlags();
        return tmpSMS;
    }
    else {
        ofxSMSMessage blank; //safety catch - returns a blank object
        return blank;
    }
}

int ofxSMS::getUnreadN(){
    return unread.size();
}

bool ofxSMS::bUnreadSMS(){
    if(unread.size()>0){
        return true;
    }
    else {
        return false;
    }
}

ofxSMSMessage ofxSMS::getSMS(string id){

    //search for the SMS by ID in the all queue and then return.

    //now change the all message accordingly.
    std::vector<ofxSMSMessage>::iterator allSMSMessage;
    for(allSMSMessage = all.begin(); allSMSMessage != all.end(); allSMSMessage++){
        if (allSMSMessage->id == id) {
            return *allSMSMessage;
        }
    }
}


